package com.crgp.game;

import java.util.Map;

import javax.swing.JPanel;

import com.crgp.game.action.PlayEndAction;
import com.crgp.game.client.Client;
import com.crgp.game.client.message.ActionMessage;
import com.crgp.game.client.message.Message;

public abstract class BaseGame {
	protected String[] robots;

	protected Client client;

	/**
	 * 标记机器人是否可点击雷区(及调用click方法).<BR>
	 * 赢或者输之后,isClickable为false;<BR>
	 * 每次play方法被调用的时候,isClickable被设置为true<BR>
	 * 每次调用click方法后,isClickable被设置为false;<BR>
	 * 执行play代码后,如果玩家的代码执行超时,isClickable被设置为false;<BR>
	 */
	private boolean isPlayable = false;

	/**
	 * 机器人是否异步执行.
	 */
	private boolean isAsyn = true;

	/**
	 * 机器人单次执行的最大时间
	 */
	private long maxPlayTime;

	private long monitorTimer = -1;// 监控线程计时器
	private Thread monitorThread;// 监控线程
	private long unit = 10;// 监控线程递减单位

	/**
	 * 运行间隔时间(运行速度)
	 */
	protected long runSpace;

	/**
	 * 运行代码，用来标示每一步执行
	 */
	private int playCode = 0;

	/**
	 * 是否可以进行下一步
	 */
	private boolean canNext = true;

	/**
	 * 是否暂停
	 */
	private boolean isPause = false;

	private boolean isRunning = false;

	public BaseGame() {
		try {
			client = new Client(GameUID());
			client.registAction("playEnd", new PlayEndAction(this));
		} catch (Exception e) {
			e.printStackTrace();
		}
		robots = new String[playerCount()];

		// 监控线程,等待一个单步时间,如果机器人没有在单步时间内结束,则强制结束机器人线程
		monitorThread = new Thread(new Runnable() {
			@Override
			public void run() {
				while (true) {
					if (monitorTimer == -1 || isPause) {
						try {
							Thread.sleep(1);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						continue;
					}
					if (monitorTimer - unit <= 0) {
						monitorTimer = -1;

						playOutTime();
						isPlayable = false;

						System.out.println("监控线程 ：超时 " + maxPlayTime + " - "
								+ monitorTimer);
					} else {
						monitorTimer -= unit;
						try {
							Thread.sleep(unit);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}
			}
		});
		monitorThread.start();
	}

	/**
	 * 返回机器人列表
	 * 
	 * @return
	 */
	public String[] getRobots() {
		return robots;
	}

	/**
	 * 返回游戏名
	 * 
	 * @return
	 */
	public abstract String getName();

	/**
	 * 返回游戏UID
	 * 
	 * @return
	 */
	public abstract String GameUID();

	/**
	 * 返回游戏需要的玩家数量
	 * 
	 * @return
	 */
	public abstract int playerCount();

	/**
	 * 返回游戏主界面
	 * 
	 * @return
	 */
	public abstract JPanel createView();

	/**
	 * 人工玩家开始方法，由子类实现。子类需要在该方法中启动人工进行游戏的功能，如事件等相关逻辑。<BR>
	 */
	public abstract void playerBegin();

	/**
	 * 人工玩家结束方法，由子类实现。子类需要在该方法中关闭人工进行游戏的功能，如事件等相关逻辑。<BR>
	 */
	public abstract void playerEnd();

	public void launch(Map<String, String> params) {
		isPlayable = false;
		playCode = 0;
		monitorTimer = -1;
	}

	public final void run() {
		isRunning = true;
		isPause = false;
		if (isAsyn) {
			// 如果是异步执行的,单次只有一个机器人的play代码在执行
			// 循环所有机器人,让每个机器人按顺序依次执行play代码
			new Thread(new Runnable() {

				@Override
				public void run() {
					while (true) {
						for (int i = 0; i < robots.length; i++) {
							String robot = robots[i];

							isPlayable = true;
							playCode++;

							if (robot != null) {
								monitorTimer = getMaxPlayTime();
								play(robot, playCode);
							} else {
								monitorTimer = -1;
								playerBegin();
							}

							waitForPlay();

							if (isEnd() || !isRunning) {
								isPlayable = false;
								doEnd();
								return;
							}
						}
					}
				}
			}).start();
		} else {
			// 如果不是异步执行的,单次所有机器人的play代码都同时执行
			// 循环所有机器人,让每个机器人同时执行play代码
			new Thread(new Runnable() {

				@Override
				public void run() {
					while (true) {
						isPlayable = true;

						for (int i = 0; i < robots.length; i++) {
							playCode++;
							String robot = robots[i];

							play(robot, playCode);
						}

						monitorTimer = getMaxPlayTime();

						waitForPlay();

						if (isEnd() || !isRunning) {
							isPlayable = false;
							doEnd();
							return;
						}
					}
				}
			}).start();
		}
	}

	public final void next(final int steps) {
		isRunning = true;
		canNext = false;

		if (isAsyn) {
			new Thread(new Runnable() {

				@Override
				public void run() {
					int stepsTmp = steps;
					boolean isWhile = true;
					while (isWhile) {
						// 如果是异步执行的,让每个机器人按顺序依次执行play代码
						for (int i = 0; i < robots.length; i++) {
							String robot = robots[i];

							isPlayable = true;
							playCode++;
							if (robot != null) {
								monitorTimer = getMaxPlayTime();
								play(robot, playCode);
							} else {
								monitorTimer = -1;
								playerBegin();
							}

							waitForPlay();

							if (isEnd() || !isRunning) {
								isPlayable = false;
								isWhile = false;
								canNext = true;
								doEnd();
								return;
							}
							stepsTmp--;
							if (stepsTmp <= 0) {
								isRunning = false;
								isWhile = false;
								canNext = true;
								break;
							}
						}
					}
				}
			}).start();
		} else {
			new Thread(new Runnable() {

				@Override
				public void run() {
					int stepsTmp = steps;
					boolean isWhile = true;
					while (isWhile) {
						// 如果不是异步执行的,让每个机器人同时执行play代码
						isPlayable = true;

						playCode++;
						for (int i = 0; i < robots.length; i++) {
							String robot = robots[i];
							play(robot, playCode);
						}

						monitorTimer = getMaxPlayTime();

						waitForPlay();

						if (isEnd() || !isRunning) {
							isPlayable = false;
							isWhile = false;
							canNext = true;
							doEnd();
							return;
						}

						stepsTmp--;
						if (stepsTmp <= 0) {
							isRunning = false;
							isWhile = false;
							canNext = true;
							break;
						}
					}
				}
			}).start();
		}
	}

	/**
	 * 标记游戏是否结束，赢或者输游戏都将结束
	 */
	public abstract boolean isEnd();

	/**
	 * 玩家代码执行完成回调方法
	 * 
	 * @param playResult
	 */
	public abstract void playEnd(Map<String, String> playResult);

	/**
	 * 玩家代码执行超时回调方法
	 * 
	 * @param playResult
	 */
	public abstract void playOutTime();

	/**
	 * 结束单步游戏.<BR>
	 * 机器人代码执行时,在playEndAction中,调用完由游戏实现类实现的playEnd方法后悔自动调用该方法.<BR>
	 * 人工玩家进行游戏时,游戏子类应该根据游戏自身的逻辑,在玩家进行"一步"人工操作后,调用该方法
	 */
	public void endPlay() {
		playerEnd();
		isPlayable = false;
	}

	private void waitForPlay() {
		// 等待上一次机器人代码执行结束
		while ((isPlayable || isPause) && isRunning) {
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		// 等待一个命令间隔
		try {
			Thread.sleep(runSpace);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public void pause() {
		isPause = true;
	}

	public void resume() {
		isPause = false;
	}

	public void stop() {
		isRunning = false;
	}

	private void doEnd() {
		// [TODO]游戏结束，计算游戏结果
	}

	/**
	 * 获取运行间隔时间
	 * 
	 * @param runSpace
	 */
	public long getRunSpace() {
		return runSpace;
	}

	/**
	 * 设置运行间隔时间
	 * 
	 * @param runSpace
	 */
	public void setRunSpace(long runSpace) {
		this.runSpace = runSpace;
	}

	/**
	 * 查询机器人是否异步执行
	 * 
	 * @return
	 */
	public boolean isAsyn() {
		return isAsyn;
	}

	/**
	 * 设置机器人是否异步执行
	 * 
	 * @param isAsyn
	 */
	public void setAsyn(boolean isAsyn) {
		this.isAsyn = isAsyn;
	}

	public boolean isCanNext() {
		return canNext;
	}

	public long getMaxPlayTime() {
		return maxPlayTime;
	}

	public void setMaxPlayTime(long maxPlayTime) {
		this.maxPlayTime = maxPlayTime;
	}

	public int getPlayCode() {
		return playCode;
	}

	// -------------------------
	// ---
	// ---信号与槽(Signal and Slot)
	// ---
	// -------------------------
	/**
	 * [SIGNAL]要求机器人发送执行代码
	 * 
	 * @param code
	 */
	public synchronized void play(String robotUID, int code) {
		ActionMessage actionMessage = new ActionMessage(Message.TYPE_GAME,
				GameUID(), Message.TYPE_ROBOT, robotUID);
		actionMessage.setActionName("play");
		actionMessage.addParam("code", code + "");
		actionMessage.setHasReceipt(false);

		client.send(actionMessage);
	}
}
